﻿using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using RotaractProjectCalendar.DataAccessLayer;
using RotaractProjectCalendar.Models;
using RotaractProjectCalendar.ViewModels;

namespace RotaractProjectCalendar.Areas.User.Controllers
{
    [Authorize(Roles = "User")]
    public class ProjectController : Controller
    {
        private readonly CalendarContext _db = new CalendarContext();

        //
        // GET: /User/Project/
        public ActionResult Index(int? id, int? clubId)
        {
            var viewModel = new ProjectIndexData
            {
                Projects = _db.Projects
                            .OrderBy(p => p.StartDateTime)
            };

            if(id != null)
            {
                ViewBag.ProjectID = id.Value;
                viewModel.Club = viewModel.Projects.Single(p => p.Id == id.Value).Club;
            }

            if (clubId == null)
            {
                return View(viewModel);
            }

            ViewBag.ClubId = clubId.Value;
            viewModel.Coordinator = viewModel.Projects
                .Single(c => id != null && (c.Coordinator.ClubId == clubId.Value && c.Coordinator.Id == c.CoordinatorId && c.Id == id.Value))
                .Coordinator;

            return View(viewModel);
        }

        //
        // GET: /User/MyProjects/
        public ActionResult MyProjects()
        {
            var viewModel = new ProjectIndexData();

            var logonUser = _db.Users.Single(u => u.UserName == User.Identity.Name);

            viewModel.Projects = _db.Projects.Where(p => p.ClubId == logonUser.ClubId)
                                            .Include(p => p.Club)
                                            .Include(p => p.Coordinator)
                                            .Include(p => p.City)
                                            .OrderBy(p => p.StartDateTime);

            return View(viewModel);
        }

        //
        // GET: /User/Project/Details/5
        public async Task<ActionResult> Details(int? id)
        {
            if(id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var project = await _db.Projects.FindAsync(id);
            if(project == null)
            {
                return HttpNotFound();
            }
            return View(project);
        }

        //
        // GET: /User/Project/Create
        public ActionResult Create()
        {
            var logonUser = _db.Users.Single(u => u.UserName == User.Identity.Name);

            PopulateCoordinatorCityDropdownLists(logonUser.ClubId);
            return View();
        }

        //
        // POST: /User/Project/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Create([Bind(Include = "CoordinatorId,CityId,ProjectTitle,Description,StartDateTime,EndDateTime,Location")] Project project)
        {
            var logonUser = _db.Users.Single(u => u.UserName == User.Identity.Name);

            project.ClubId = logonUser.ClubId;

            project.Photo = "~/Content/Images/rac.jpg";

            project.Modified = DateTime.UtcNow;

            project.IpAddress = GetUserIp();

            try
            {
                if(ModelState.IsValid)
                {
                    foreach (var upload in Request.Files.Cast<string>()
                        .Where(upload => HasFile(Request.Files[upload])))
                        {
                            const string path = "~/Content/Images/";

                            var httpPostedFileBase = Request.Files[upload];

                            Debug.Assert(httpPostedFileBase != null, "httpPostedFileBase != null");
                            var filename = Path.GetFileName(httpPostedFileBase.FileName);

                            if(!httpPostedFileBase.ContentType.StartsWith("image"))
                            {
                                ModelState.AddModelError("", "Unable to create event. The attachment have to an image.");

                                PopulateCoordinatorCityDropdownLists(project.ClubId, project.ClubId, project.CoordinatorId, project.CityId);
                                return View(project);
                            }

                            if(httpPostedFileBase.ContentLength > 5000000)
                            {
                                ModelState.AddModelError("", "Unable to create event. The attachment is too big. Maximum size of the file 5 MB.");

                                PopulateCoordinatorCityDropdownLists(project.ClubId, project.ClubId, project.CoordinatorId, project.CityId);
                                return View(project);
                            }

                            if (filename == null)
                            {
                                continue;
                            }

                            httpPostedFileBase.SaveAs(Server.MapPath(Path.Combine(path, filename)));

                            project.Photo = Path.Combine(path, filename);
                        }

                    _db.Projects.Add(project);
                    await _db.SaveChangesAsync();
                    return RedirectToAction("MyProjects");
                }
            }
            catch(RetryLimitExceededException /* dex */)
            {
                //Log the error (uncomment dex variable name and add a line here to write a log.
                ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
            }
            PopulateCoordinatorCityDropdownLists(project.ClubId, project.ClubId, project.CoordinatorId, project.CityId);
            return View(project);
        }

        //
        // GET: /Project/Edit/5
        public async Task<ActionResult> Edit(int? id)
        {
            if(id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var project = await _db.Projects.FindAsync(id);
            if(project == null)
            {
                return HttpNotFound();
            }
            PopulateCoordinatorCityDropdownLists(project.ClubId, project.ClubId, project.CoordinatorId, project.CityId);
            return View(project);
        }

        //
        // POST: /Project/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Edit([Bind(Include = "ID,ClubId,CoordinatorId,CityId,ProjectTitle,Description,Photo,StartDateTime,EndDateTime,Location,Modified,IpAddress")] Project project)
        {
            try
            {
                if(ModelState.IsValid)
                {
                    project.Modified = DateTime.UtcNow;

                    project.IpAddress = GetUserIp();

                    var logonUser = _db.Users.Single(u => u.UserName == User.Identity.Name);
                    if(project.ClubId != logonUser.ClubId)
                    {
                        PopulateCoordinatorCityDropdownLists(project.ClubId, project.ClubId, project.CoordinatorId, project.CityId);
                        return View(project);
                    }

                    foreach (var upload in Request.Files.Cast<string>()
                        .Where(upload => HasFile(Request.Files[upload])))
                        {
                            const string path = "~/Content/Images/";

                            var httpPostedFileBase = Request.Files[upload];

                            Debug.Assert(httpPostedFileBase != null, "httpPostedFileBase != null");
                            var filename = Path.GetFileName(httpPostedFileBase.FileName);

                            if(!httpPostedFileBase.ContentType.StartsWith("image"))
                            {
                                ModelState.AddModelError("", "Unable to save changes. The attachment have one of the selected type: .jpg, .png or .gif");

                                PopulateCoordinatorCityDropdownLists(project.ClubId, project.ClubId, project.CoordinatorId, project.CityId);
                                return View(project);
                            }

                            if(httpPostedFileBase.ContentLength > 5000000)
                            {
                                ModelState.AddModelError("", "Unable to save changes. The attachment is too big. Maximum size of the file 5 MB.");

                                PopulateCoordinatorCityDropdownLists(project.ClubId, project.ClubId, project.CoordinatorId, project.CityId);
                                return View(project);
                            }

                            if (filename == null)
                            {
                                continue;
                            }

                            httpPostedFileBase.SaveAs(Server.MapPath(Path.Combine(path, filename)));

                            project.Photo = Path.Combine(path, filename);
                        }

                    _db.Entry(project).State = EntityState.Modified;
                    await _db.SaveChangesAsync();
                    return RedirectToAction("MyProjects");
                }
            }
            catch(RetryLimitExceededException /* dex */)
            {
                //Log the error (uncomment dex variable name and add a line here to write a log.
                ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
            }
            PopulateCoordinatorCityDropdownLists(project.ClubId, project.ClubId, project.CoordinatorId, project.CityId);
            return View(project);
        }

        //
        // GET: /User/Project/Delete/5
        public async Task<ActionResult> Delete(int? id, bool? saveChangesError = false)
        {
            if(id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            if(saveChangesError.GetValueOrDefault())
            {
                ViewBag.ErrorMessage = "Deleted failed. Try again, and if the problem persist see your system administrator.";
            }
            var project = await _db.Projects.FindAsync(id);
            if(project == null)
            {
                return HttpNotFound();
            }
            return View(project);
        }

        //
        // POST: /User/Project/Delete/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Delete(int id)
        {
            try
            {
                var project = await _db.Projects.FindAsync(id);
                var logonUser = _db.Users.Single(u => u.UserName == User.Identity.Name);
                if (project.ClubId != logonUser.ClubId)
                {
                    return RedirectToAction("Delete", new { id, saveChangesError = true });
                }

                _db.Projects.Remove(project);

                if(!String.IsNullOrWhiteSpace(project.Photo))
                {
                    System.IO.File.Delete(Server.MapPath(project.Photo));
                }

                await _db.SaveChangesAsync();
            }
            catch(RetryLimitExceededException /* dex */)
            {
                //Log the error (uncomment dex variable name and add a line here to write a log.
                return RedirectToAction("Delete", new { id, saveChangesError = true });
            }

            return RedirectToAction("MyProjects");
        }

        [ChildActionOnly]
        private string GetUserIp()
        {
            var ipList = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

            return !string.IsNullOrEmpty(ipList) 
                ? ipList.Split(',')[0] 
                : Request.ServerVariables["REMOTE_ADDR"];
        }

        [ChildActionOnly]
        private static bool HasFile(HttpPostedFileBase file)
        {
            return (file != null && file.ContentLength > 1);
        }

        [ChildActionOnly]
        private void PopulateCoordinatorCityDropdownLists(int? clubId, object selectedClub = null, object selectedCoordinator = null, object selectedCity = null)
        {
            var coordinatorsQuery = _db.Coordinators.Where(c => c.ClubId == clubId).OrderBy(c => c.Name);

            ViewBag.ClubId = new SelectList(_db.Clubs, "ID", "ClubTitle", selectedClub);
            ViewBag.CoordinatorId = new SelectList(coordinatorsQuery, "ID", "Name", selectedCoordinator);
            ViewBag.CityId = new SelectList(_db.Cities, "ID", "CityName", selectedCity);
        }

        protected override void Dispose(bool disposing)
        {
            if(disposing)
            {
                _db.Dispose();
            }
            base.Dispose(disposing);
        }
	}
}